#pragma once

#include <vex/core/config.h>
#include <vex/core/contract/memory_resource.h>
#include <vex/core/implementation/ownership_policies.h>
#include <vex/core/as_intrusive.h>
#include <vex/core/as_raw.h>
#include <type_traits>
#include <functional>

#if defined(_MSC_VER)
#pragma warning (push)
#endif

namespace vex {
    namespace core {
        namespace implementation {

            /// <summary>
            /// Implementation of handle created from a user-defined base class <paramref name="TBase"/>
            /// which implements at least the <code>handle</code> contract. <paramref name="TBase"/> 
            /// might implement <code>memory_resource_owner</code> contract.
            /// </summary>
            template<
                class TBase,
                class TOwnershipPolicy = shared_synchronized,
                bool TBaseIsMemoryResourceOwner = std::is_base_of<contract::memory_resource_owner, TBase>::value
            >
            class handle_from_base;

            /// <summary>
            /// Specialization for the variant in which <paramref name="TBase"/> 
            /// does not implement <code>memory_resource_owner</code> contract
            /// </summary>
            template<class TBase, class TOwnershipPolicy>
            class handle_from_base<TBase, TOwnershipPolicy, false>
                : public TBase, TOwnershipPolicy
            {
                typedef handle_from_base<TBase, TOwnershipPolicy, false> this_type;

                handle_from_base(handle_from_base const &);
                handle_from_base& operator = (handle_from_base const &);
            public:
                virtual void add_ref()
                {
                    this->TOwnershipPolicy::add_ref();
                }

                virtual void release()
                {
                    this->TOwnershipPolicy::release([=]() {
                        delete this;
                    });
                }

                virtual bool has_synchronized_ref_count() const
                {
                    return this->TOwnershipPolicy::has_synchronized_ref_count();
                }

                virtual bool is_shareable() const
                {
                    return this->TOwnershipPolicy::is_shareable();
                }

#ifdef VEX_HAS_CPP_11_FEATURE_SET_3
#if defined(_MSC_VER) && _MSC_VER == 1800
#pragma warning( disable : 4520 )

                handle_from_base()
                    : TBase()
                {
                }
#endif

                template <class ...TArgs>
                handle_from_base(TArgs&& ...p_args)
                    : TBase(std::forward<TArgs>(p_args)...)
                {
                }
#else
                handle_from_base()
                    : TBase()
                {
                }

                template <class TArg1>
                handle_from_base(TArg1 && p_arg1)
                    : TBase(std::forward<TArg1>(p_arg1))
                {
                }

                template <class TArg1, class TArg2>
                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2)
                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2))
                {
                }

                template <class TArg1, class TArg2, class TArg3>
                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3)
                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3))
                {
                }

                template <class TArg1, class TArg2, class TArg3, class TArg4>
                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4)
                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3), std::forward<TArg4>(p_arg4))
                {
                }

                template <class TArg1, class TArg2, class TArg3, class TArg4, class TArg5>
                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4, TArg5 && p_arg5)
                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3), std::forward<TArg4>(p_arg4), std::forward<TArg5>(p_arg5))
                {
                }
#endif

                ~handle_from_base()
#if defined(VEX_TESTS_ACTIVE) && defined(_DEBUG)
                {
                    std::stringstream t_out;
                    t_out << __FUNCTION__ << std::endl;
                    Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(t_out.str().c_str());
                }
#else
                {}
#endif
            };

            //        struct deleter_arg_t {};
            //
            //        const deleter_arg_t deleter_arg = deleter_arg_t();
            //
            //        namespace implementation {
            //
            //            /// <summary>
            //            /// Implementation of handle created from a user-defined base class <paramref name="TBase"/>
            //            /// which implements at least the <code>handle</code> contract. <paramref name="TBase"/> 
            //            /// might implement <code>memory_resource_owner</code> contract.
            //            /// </summary>
            //            template<
            //                class TBase,
            //                class TOwnershipPolicy = shared_synchronized,
            //                    bool TBaseIsMemoryResourceOwner = std::is_base_of<contract::memory_resource_owner, TBase>::value
            //            >
            //            class handle_from_base;
            //
            //            /// <summary>
            //            /// Specialization for the variant in which <paramref name="TBase"/> 
            //            /// does not implement <code>memory_resource_owner</code> contract
            //            /// </summary>
            //            template<class TBase, class TOwnershipPolicy>
            //            class handle_from_base<TBase, TOwnershipPolicy, false>
            //                : public TBase, TOwnershipPolicy
            //            {
            //                typedef handle_from_base<TBase, TOwnershipPolicy, false> this_type;
            //                typedef std::function<void(this_type*)> deleter_type;
            //
            //                deleter_type m_deleter;
            //
            //                handle_from_base(handle_from_base const &);
            //                handle_from_base& operator = (handle_from_base const &);
            //            public:
            //                virtual void add_ref()
            //                {
            //                    this->TOwnershipPolicy::add_ref();
            //                }
            //
            //                virtual void release()
            //                {
            //                    this->TOwnershipPolicy::release([=]() {
            //                        // TODO: this needs to be measured - is a branch or a virtual function call cheaper?
            //                        if (m_deleter) {
            //                            // custom deleter is present ans assigned => copy and invoke deleter:
            //                            auto t_deleter = m_deleter;
            //                            t_deleter(this);
            //                        } else {
            //                            // the default case, custom deleter is not assigned...
            //                            delete this;
            //                        }
            //                    });
            //                }
            //
            //                virtual bool has_synchronized_ref_count() const
            //                {
            //                    return this->TOwnershipPolicy::has_synchronized_ref_count();
            //                }
            //
            //                virtual bool is_shareable() const
            //                {
            //                    return this->TOwnershipPolicy::is_shareable();
            //                }
            //
            //#ifdef VEX_HAS_CPP_11_FEATURE_SET_3
            //                handle_from_base()
            //                    : TBase()
            //                {
            //                }
            //
            //                template <class ...TArgs>
            //                handle_from_base(TArgs&& ...p_args)
            //                    : TBase(std::forward<TArgs>(p_args)...)
            //                {
            //                }
            //
            //                template <class ...TArgs>
            //                handle_from_base(deleter_arg_t const&, deleter_type p_deleter, TArgs&& ...p_args)
            //                    : TBase(std::forward<TArgs>(p_args)...)
            //                    , m_deleter(std::move(p_deleter))
            //                {
            //                }
            //#else
            //                handle_from_base() 
            //                    : TBase() {}
            //
            //                template <class TArg1>
            //                handle_from_base(TArg1 && p_arg1) 
            //                    : TBase(std::forward<TArg1>(p_arg1)) {}
            //
            //                template <class TArg1, class TArg2>
            //                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2) 
            //                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2)) {}
            //
            //                template <class TArg1, class TArg2, class TArg3>
            //                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
            //                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3)) {}
            //
            //                template <class TArg1, class TArg2, class TArg3, class TArg4>
            //                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
            //                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3), std::forward<TArg4>(p_arg4)) {}
            //
            //                template <class TArg1, class TArg2, class TArg3, class TArg4, class TArg5>
            //                handle_from_base(TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4, TArg5 && p_arg5) 
            //                    : TBase(std::forward<TArg1>(p_arg1), std::forward<TArg2>(p_arg2), std::forward<TArg3>(p_arg3), std::forward<TArg4>(p_arg4), std::forward<TArg5>(p_arg5)) {}
            //#endif
            //
            //                ~handle_from_base()
            //#if defined(VEX_TESTS_ACTIVE) && defined(_DEBUG)
            //                {
            //                    std::stringstream t_out;
            //                    t_out << __FUNCTION__ << std::endl;
            //                    Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(t_out.str().c_str());
            //                }
            //#elif VEX_HAS_CPP_11_FEATURE_SET_3
            //                    = default;
            //#else
            //                {}
            //#endif
            //            };
            //
            //            /// <summary>
            //            /// Specialization for the variant in which <paramref name="TBase"/> 
            //            /// implements <code>memory_resource_owner</code> contract
            //            /// </summary>
            //            template<class TBase, class TOwnershipPolicy>
            //            class handle_from_base<TBase, TOwnershipPolicy, true>
            //                : public handle_from_base<TBase, TOwnershipPolicy, false>
            //            {
            //                typedef handle_from_base<TBase, TOwnershipPolicy, true> this_type;
            //                typedef handle_from_base<TBase, TOwnershipPolicy, false> base_type;
            //
            //                typedef boost::intrusive_ptr<contract::memory_resource> mem_resource_holder_type;
            //
            //                mutable mem_resource_holder_type m_mem_resource;
            //            public:
            //                virtual contract::memory_resource* get_memory_resource() const
            //                {
            //                    return vex::as_raw_out(m_mem_resource);
            //                }
            //
            //#ifdef VEX_HAS_CPP_11_FEATURE_SET_3
            //                template <class ...TArgs>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArgs&& ...p_args)
            //                    : base_type(p_mem_res, std::forward<TArgs>(p_args)...)
            //                    , m_mem_resource(p_mem_res, true)
            //                {
            //                }
            //
            //                template <class ...TArgs> handle_from_base(
            //                    deleter_arg_t const&,
            //                    deleter_type p_deleter,
            //                    contract::memory_resource* p_mem_res,
            //                    TArgs&& ...p_args)
            //                    : base_type(deleter_arg, p_deleter, p_mem_res, std::forward<TArgs>(p_args)...)
            //                    , m_mem_resource(p_mem_res, true)
            //                {
            //                }
            //#else
            //                handle_from_base(contract::memory_resource* p_mem_res) 
            //                    : base_type()
            //                    , m_mem_resource(p_mem_res, true) {}
            //
            //                template <class TArg1>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArg1 && p_arg1) 
            //                    : base_type(std::forward<TArg1>(p_arg1))
            //                    , m_mem_resource(p_mem_res, true) {}
            //
            //                template <class TArg1, class TArg2>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArg1 && p_arg1, TArg2 && p_arg2) 
            //                    : base_type(
            //                    std::forward<TArg1>(p_arg1), 
            //                    std::forward<TArg2>(p_arg2))
            //                    , m_mem_resource(p_mem_res, true) {}
            //
            //                template <class TArg1, class TArg2, class TArg3>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
            //                    : base_type(
            //                    std::forward<TArg1>(p_arg1), 
            //                    std::forward<TArg2>(p_arg2), 
            //                    std::forward<TArg3>(p_arg3))
            //                    , m_mem_resource(p_mem_res, true) {}
            //
            //                template <class TArg1, class TArg2, class TArg3, class TArg4>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
            //                    : base_type(
            //                    std::forward<TArg1>(p_arg1), 
            //                    std::forward<TArg2>(p_arg2), 
            //                    std::forward<TArg3>(p_arg3), 
            //                    std::forward<TArg4>(p_arg4))
            //                    , m_mem_resource(p_mem_res, true) {}
            //
            //                template <class TArg1, class TArg2, class TArg3, class TArg4, class TArg5>
            //                handle_from_base(contract::memory_resource* p_mem_res, TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4, TArg5 && p_arg5) 
            //                    : base_type(
            //                    std::forward<TArg1>(p_arg1), 
            //                    std::forward<TArg2>(p_arg2), 
            //                    std::forward<TArg3>(p_arg3), 
            //                    std::forward<TArg4>(p_arg4), 
            //                    std::forward<TArg5>(p_arg5))
            //                    , m_mem_resource(p_mem_res, true) {}
            //#endif
            //
            //                ~handle_from_base()
            //#if defined(VEX_TESTS_ACTIVE) && defined(_DEBUG)
            //                {
            //                    std::stringstream t_out;
            //                    t_out << __FUNCTION__ << std::endl;
            //                    Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(t_out.str().c_str());
            //                }
            //#elif VEX_HAS_CPP_11_FEATURE_SET_3
            //                    = default;
            //#else
            //                {}
            //#endif
            //            };
            //        }
        }
    }

    // import into vex
    using core::implementation::handle_from_base;
}
#if defined(_MSC_VER)
#pragma warning (pop)
#endif